#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Ce module contient plusieurs classes permettant de gérer les éléments 
principaux d'un jeu de carte: la carte avec sa figure et sa sorte, une
donne sous la forme d'une série de cartes, et un paquet standard de
52 cartes.
"""
__auteur__ = ""
__coequipiers__ = None
__date__ = ""

class Carte:
    """ Cette classe encapsule une carte avec sa figure et sa sorte. """
    figures = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
    sortes = {'pique': '♠', 'coeur': '♥', 'carreau': '♦', 'trèfle': '♣'}

    def __init__(self, figure, sorte, face_visible=False):
        """ Construire une carte à partir d'une figure et d'une sorte. 
        Par défaut, la face de la carte est cachée. 

        :param figure: une chaîne d'un seul caractère spécifiant la figure de 
        la carte. Les figures possibles sont 'A', '2', '3', '4', '5', '6', '7', 
        '8', '9', '10', 'J', 'Q', 'K'.
        :param sorte: une chaîne de caractères spécifiant la sorte de carte.
        Les sortes possibles sont 'pique', 'coeur', 'carreau' et 'trèfle'.
        :param face_visible: bouléen spécifiant si la face de la carte est
        visible ou non (par défaut: non).
        :returns: rien.
        """
        self.figure = figure
        self.sorte = sorte
        self.face_visible = face_visible

    def __str__(self):
        """ Convertir en chaîne de caractères. 

        :returns: la carte sous la forme «FS», où «F» représente la figure 
        (parmi 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'), 
        et «S» représente la sorte (parmi '♠', '♥', '♦', '♣'), si la face 
        est visible; retourne «??» autrement.
        """
        if self.face_visible:
            return self.figure+Carte.sortes[self.sorte]
        else:
            return '??'

    def flipper(self):
        """ Renverser la face de la carte. 

        :returns: la carte self.
        """
        self.face_visible = not self.face_visible
        return self

class Donne:
    """ Cette classe encapsule une donne, c-à-d une séquence de cartes. """

    def __init__(self):
        """ Construire une donne vide. Une donne est une liste de cartes. """
        self.cartes = []

    def __str__(self):
        """ Convertir en chaîne de caractères. 

        :returns: la donne sous la forme d'une séquence de cartes séparées par
        des espaces; None dans le cas d'une donne vide.
        """
        if self.cartes:
            donne = str(self.cartes[0])
            for carte in self.cartes[1:]:
                donne += ' ' + str(carte)
        else:
            donne = None
        return donne

    def __add__(self, donne):
        """ Concaténer deux donnes en une seule.

        :param donne: la donne à concaténer avec self.
        :returns: la concaténation des deux donnes.
        """
        resultat = Donne()
        resultat.cartes = list(self.cartes)
        resultat += donne
        return resultat

    def __iadd__(self, donne):
        """ Concaténer deux donnes en une seule en stockant le résultat
        dans la première donne.

        :param donne: la donne à concaténer avec self.
        :returns: rien.
        """
        for carte in donne.cartes:
            self.ajouter(carte)
        return self

    def ajouter(self, carte):
        """ Ajouter une nouvelle carte à la donne.

        :param carte: la carte à ajouter.
        :returns: rien.
        """
        self.cartes.append(carte)

    def retirer(self, carte):
        """ Retirer une carte de la donne

        :param carte: la carte à retirer.
        :returns: rien
        """
        self.cartes.remove(carte)

    def vider(self):
        """ Retirer toutes les cartes de la donne. 

        :returns: rien.
        """
        self.cartes = []

class Paquet(Donne):
    """ Cette classe encapsule un paquet standard de 52 cartes. """

    def __init__(self, carte=Carte):
        """ Construire un paquet de 52 cartes prémélangées. 

        :param carte: le type de carte à insérer dans le paquet
        (doit être dérivé de Carte).
        :returns: rien.
        """
        super().__init__()
        self.carte = carte
        self.renouveler()
        self.melanger()

    def melanger(self):
        """ Mélanger aléatoirement les cartes. 

        :returns: rien.
        """
        import random
        random.shuffle(self.cartes)

    def renouveler(self):
        """ Renouveler les 52 cartes sans les mélanger. 

        :returns: rien.
        """
        self.vider()
        for figure in Carte.figures:
            for sorte in Carte.sortes.keys():
                self.ajouter(self.carte(figure, sorte))

    def tirer(self):
        """ Tirer une carte du paquet.

        :returns: la carte sur le dessus du paquet. Si le paquet est vide,
        renouvelle et mélange automatiquement un nouveau paquet.
        """
        if not self.cartes:
            self.renouveler()
            self.melanger()
        return self.cartes.pop()

if __name__ == '__main__':
    # programme principal
    print("Le module JeuDeCarte n'est pas conçu pour être exécuté en tant que programme principal")
